using Godot;

#pragma warning disable CS8618 // 在退出构造函数时，不可为 null 的字段必须包含非 null 值。请考虑添加 'required' 修饰符或声明为可以为 null。

namespace MyCraft2;

public partial class Player : CharacterBody3D
{
    [Export] public float RotationSensitivity { get; set; } = 0.1f;
    [Export] public float MoveSpeed { get; set; } = 2f;
    [Export] public float JumpForce = 10;
    [Export] public float Gravity = 50;
    [Export] public Node3D? CameraPivot;
    [Export] public Camera3D? Camera3d;
    [Export] public RichTextLabel? PositionLabel;
    private PositionLabel? _positionLabel;
    
    private CameraAngle _cameraAngle = CameraAngle.FirstPerson;
    [Export]
    public CameraAngle CameraAngle
    {
        get => _cameraAngle;
        set
        {
            _cameraAngle = value;
            SetCameraAngle();
        }
    }

    public bool CanControlPlayer =>
        Input.MouseMode is Input.MouseModeEnum.Captured && ReferenceEquals(GetViewport().GetCamera3D(), Camera3d);
    
    private bool _isReady;
    private Node3D _model;
    private Node3D _headJoint;
    private AnimationPlayer _animationPlayer;
    private CollisionShape3D _collisionShape3D;

    public override void _Ready()
    {
        base._Ready();
        if (!Engine.IsEditorHint())
            Input.MouseMode = Input.MouseModeEnum.Captured;

        SkinReady();

        _model = GetNode<Node3D>("Model");
        _headJoint = GetNode<Node3D>("Model/HeadJoint");
        _animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
        _collisionShape3D = GetNode<CollisionShape3D>("CollisionShape3D");
        
        if(PositionLabel is not null)
        {
            _positionLabel ??= (PositionLabel as PositionLabel)!;
            _positionLabel.PositionInText = Position;
        }

        _isReady = true;
        SetCameraAngle();

        InputMapExtend.AddAction("move_forward", new InputEventKey { Keycode = Key.W });
        InputMapExtend.AddAction("move_back", new InputEventKey { Keycode = Key.S });
        InputMapExtend.AddAction("move_left", new InputEventKey { Keycode = Key.A });
        InputMapExtend.AddAction("move_right", new InputEventKey { Keycode = Key.D });

        InputMapExtend.AddAction("jump", new InputEventKey { Keycode = Key.Space });

        InputMapExtend.AddAction("change_camera_angle", new InputEventKey { Keycode = Key.R });

        InputMapExtend.AddAction("reset", new InputEventKey { Keycode = Key.Enter });
    }

    private const float MaxDeg = 85;

    public override void _Input(InputEvent @event)
    {
        base._Input(@event);
        
        if (CanControlPlayer && Input.IsActionJustPressed("change_camera_angle"))
        {
            CameraAngle = CameraAngle switch
            {
                CameraAngle.FirstPerson => CameraAngle.ThirdPerson,
                CameraAngle.ThirdPerson => CameraAngle.FirstPerson,
                _ => CameraAngle
            };
        }

        if (CanControlPlayer && Input.IsActionJustPressed("reset"))
            Position = new Vector3(0, 4.5f, 0);

        if (CameraPivot is null || Camera3d is null) return;

        if (CanControlPlayer && @event is InputEventMouseMotion mouseMotion)
        {
            var delta = mouseMotion.Relative * RotationSensitivity;
            
            RotateView(delta);
        }
    }

    public bool IsMoving { get; private set; }

    public override void _PhysicsProcess(double delta)
    {
        base._PhysicsProcess(delta);

        // 重力处理
        if (!IsOnFloor()) Velocity += new Vector3(0, -Gravity * (float)delta, 0);
        else Velocity = new Vector3(Velocity.X, Mathf.Max(Velocity.Y, 0), Velocity.Z);
        // 跳跃（保留水平速度）
        if (CanControlPlayer && IsOnFloor() && Input.IsActionPressed("jump"))
            Velocity = new Vector3(Velocity.X, JumpForce, Velocity.Z);

        if (CameraPivot is null || Camera3d is null) return;

        // 输入检测
        var inputDirection = Vector3.Zero;
        if (Camera3d != null && CanControlPlayer)
        {
            if (Input.IsActionPressed("move_forward"))
                inputDirection += -Camera3d.GlobalTransform.Basis.Z.Normalized();
            if (Input.IsActionPressed("move_back"))
                inputDirection += Camera3d.GlobalTransform.Basis.Z.Normalized();
            if (Input.IsActionPressed("move_left"))
                inputDirection += -Camera3d.GlobalTransform.Basis.X.Normalized();
            if (Input.IsActionPressed("move_right"))
                inputDirection += Camera3d.GlobalTransform.Basis.X.Normalized();
        }

        // 移动处理
        if (inputDirection != Vector3.Zero)
        {
            IsMoving = true;

            var horizontalDir = new Vector3(inputDirection.X, 0, inputDirection.Z).Normalized();

            if (!Input.IsActionPressed("move_back"))
            {
                // 模型转向
                var cgr = CameraPivot.GlobalRotation;
                LookAt(_model.GlobalPosition + horizontalDir, Vector3.Up);
                CameraPivot.GlobalRotation = cgr;
                if(CameraAngle is CameraAngle.ThirdPerson)UpdateHeadJointRotation();
            }

            // 设置速度
            Velocity = new Vector3(
                horizontalDir.X * MoveSpeed,
                Velocity.Y,
                horizontalDir.Z * MoveSpeed
            );

            _animationPlayer.Play("walk");
        }
        else
        {
            IsMoving = false;
            // 停止移动
            Velocity = new Vector3(0, Velocity.Y, 0);
            _animationPlayer.Play("idle");
        }

        MoveAndSlide();
        if(_positionLabel is not null)_positionLabel.PositionInText = Position;
    }

    private void SetCameraAngle()
    {
        if (Camera3d is null || CameraPivot is null || !_isReady) return;
        Camera3d.Position = _cameraAngle switch
        {
            CameraAngle.FirstPerson => new Vector3(0, 0.25f, -0.25f),
            CameraAngle.ThirdPerson => new Vector3(0.5f, 0.6f, 2),
            _ => Camera3d.Position
        };
        
        _headJoint.RotationDegrees =
            CameraPivot.RotationDegrees = _model.RotationDegrees;
    }

    /// <summary>
    /// 旋转视角方法
    /// </summary>
    /// <param name="delta"></param>
    private void RotateView(Vector2 delta)
    {
        if(CameraPivot is null || Camera3d is null)return;
        
        if (CameraAngle is CameraAngle.FirstPerson)
        {
            var newXRotation = Mathf.Clamp(CameraPivot!.RotationDegrees.X - delta.Y, -MaxDeg, MaxDeg);

            var desiredYRotation = CameraPivot.RotationDegrees.Y - delta.X;
            var newYRotation = Mathf.Clamp(desiredYRotation, -MaxDeg, MaxDeg);

            var excessYRotation = desiredYRotation - newYRotation;
            CameraPivot.RotationDegrees = new Vector3(newXRotation, newYRotation , 0);

            if (excessYRotation != 0)
                RotationDegrees = new Vector3(
                    0,
                    RotationDegrees.Y + excessYRotation,
                    0
                );
        }
        else if (CameraAngle is CameraAngle.ThirdPerson)
        {
            var newX = Mathf.Clamp(CameraPivot.RotationDegrees.X - delta.Y, -MaxDeg, MaxDeg);
            var newY = (CameraPivot.RotationDegrees.Y - delta.X) % 360;
            CameraPivot.RotationDegrees = new Vector3(newX, newY, 0);
            
            UpdateHeadJointRotation();
        }
    }

    private void UpdateHeadJointRotation()
    {
        var relativeRotation = CameraPivot!.RotationDegrees - _model.RotationDegrees;
    
        _headJoint.RotationDegrees = new Vector3(relativeRotation.X, Mathf.Clamp(relativeRotation.Y, -MaxDeg, MaxDeg), 0);
    }
}